
#include <BPainter>
#include "member_BVBoxLayout.h"

using namespace BWE;

member_BVBoxLayout::member_BVBoxLayout(BVBoxLayout* layout)
{
	boss = layout;
	spacing = 1;
	pressed = false;
	splitable = false;
	handleIndex = -1;
	totalBaseHeight = 0;
}
member_BVBoxLayout::~member_BVBoxLayout()
{

}

BRect member_BVBoxLayout::handleRect(int index)
{
	ILayoutItem& item = items(index);
	return BRect(item.rect.left(), item.rect.bottom(), item.rect.width(), spacing);
}
void member_BVBoxLayout::checkHandle(int& handle)
{
	if (handle < 0)
		return;

	upLimit.reset();
	for (int i = handle; i >= 0; i--)
	{
		ILayoutItem& item = items[i];
		upLimit.max() += item.maxSize.height() - item.realSize.height();
		upLimit.min() -= item.realSize.height() - item.minSize.height();
	}
	downLimit.reset();
	for (int i = handle + 1; i < items.size(); i++)
	{
		ILayoutItem& item = items[i];
		downLimit.max() += item.maxSize.height() - item.realSize.height();
		downLimit.min() -= item.realSize.height() - item.minSize.height();
	}

	if (upLimit.empty() || downLimit.empty())
	{
		handle = -1;
	}
}
bool member_BVBoxLayout::dragHandle(const BPoint& pos)
{
	if (handleIndex < 0)
		return false;

	BPoint change = pos - pressCenter;
	upLimit.limit(change.y());
	downLimit.limit(change.y());
	if (change.y() == 0)
		return false;

	int shrink = change.y();
	for (int i = handleIndex; i >= 0; i--)
	{
		ILayoutItem& item = items[i];
		CacheSize& cache = dragCaches[i];
		cache.realSize = cache.metaSize;
		int maxHeight = item.maxSize.height();
		if (cache.realSize.height() + shrink > maxHeight)
		{
			shrink -= maxHeight - cache.realSize.height();
			cache.realSize.height() = maxHeight;
			continue;
		}
		int minHeight = item.minSize.height();
		if (cache.realSize.height() + shrink < minHeight)
		{
			shrink -= minHeight - cache.realSize.height();
			cache.realSize.height() = minHeight;
			continue;
		}
		cache.realSize.height() += shrink;
		shrink = 0;
		break;
	}
	int expand = -change.y();
	for (int i = handleIndex + 1; i < items.size(); i++)
	{
		ILayoutItem& item = items[i];
		CacheSize& cache = dragCaches[i];
		cache.realSize = cache.metaSize;
		int maxHeight = item.maxSize.height();
		if (cache.realSize.height() + expand > maxHeight)
		{
			expand -= maxHeight - cache.realSize.height();
			cache.realSize.height() = maxHeight;
			continue;
		}
		int minHeight = item.minSize.height();
		if (cache.realSize.height() + expand < minHeight)
		{
			expand -= minHeight - cache.realSize.height();
			cache.realSize.height() = minHeight;
			continue;
		}
		cache.realSize.height() += expand;
		expand = 0;
		break;
	}

	if (shrink != expand)
		return false;

	for (int i = 0; i < items.size(); i++)
	{
		ILayoutItem& item = items[i];
		CacheSize& cache = dragCaches[i];
		item.realSize = cache.realSize;
		item.rect.setSize(cache.realSize);
	}

	applyRects();

	int totalHeight = boss->rect().height() - totalBaseHeight;
	if (totalHeight)
	{
		for (int i = 0; i < items.size(); i++)
		{
			ILayoutItem& item = items[i];
			item.stretch.heightStretch() = (float)(item.realSize.height() - item.baseSize.height()) / totalHeight * totalStretch;
		}
	}
	return true;
}

void member_BVBoxLayout::distributeHeight()
{
	const BRect& rect = boss->rect();
	const BMargin& margin = boss->margin();
	int width = rect.width() - margin.width();

	const BSize& minSize = boss->minSize();
	const BSize& maxSize = boss->maxSize();

	totalStretch = 0.0f;
	int totalHeight = margin.height();
	int validCount = 0;
	for (int i = 0; i < items.size(); i++)
	{
		ILayoutItem& item = items[i];
		if (item.prepare(0, width, 0))
		{
			validCount++;
			totalHeight += item.realSize.height() + spacing;
			totalStretch += item.stretch.heightStretch();
		}
	}
	if (validCount == 0)
		return;
	totalHeight -= spacing;
	totalBaseHeight = bClamp(totalHeight, minSize.height(), maxSize.height());
	if (totalHeight != totalBaseHeight)
	{
		int remainHeight = totalBaseHeight - totalHeight;
		float totalStretch = 0;
		for (int i = 0; i < items.size(); i++)
		{
			if (!items[i].finishHeight)
				totalStretch += items[i].stretch.heightStretch();
		}
		for (int i = 0; i < items.size(); i++)
		{
			ILayoutItem& item = items[i];
			if (!item.finishHeight)
				item.realSize.height() += (int)(remainHeight * item.stretch.heightStretch() / totalStretch);
		}
	}

	int remainHeight = boss->rect().height() - totalBaseHeight;

	while (remainHeight != 0)
	{
		float totalStretch = 0;
		for (int i = 0; i < items.size(); i++)
		{
			ILayoutItem& item = items[i];
			if (item.finishHeight)
				continue;
			if (item.stretch.heightStretch() == 0.0f)
				continue;
			if (remainHeight > 0 && !(item.sizePolicy.heightPolicy() & Policy_Expanding))
			{
				item.finishHeight = true;
				continue;
			}
			if (remainHeight < 0 && !(item.sizePolicy.heightPolicy() & Policy_Shrinking))
			{
				item.finishHeight = true;
				continue;
			}
			totalStretch += item.stretch.heightStretch();
		}
		if (totalStretch < 0.00001)
			break;

		int used = 0;
		for (int i = 0; i < items.size(); i++)
		{
			ILayoutItem& item = items[i];
			if (item.finishHeight)
				continue;
			int height = (int)(item.stretch.heightStretch() / totalStretch * remainHeight);
			if (height == 0)
				continue;
			int maxHeight = item.maxSize.height();
			if (height + item.realSize.height() > maxHeight)
			{
				used += maxHeight - item.realSize.height();
				item.realSize.height() = maxHeight;
				item.finishHeight = true;
				totalStretch -= item.stretch.heightStretch();
				continue;
			}
			int minHeight = item.minSize.height();
			if (height + item.realSize.height() < minHeight)
			{
				used += minHeight - item.realSize.height();
				item.realSize.height() = minHeight;
				item.finishHeight = true;
				totalStretch -= item.stretch.heightStretch();
				continue;
			}
			item.realSize.height() += height;
			used += height;
		}
		if (used == 0)
			break;
		remainHeight -= used;
	}

	for (int i = 0; i < items.size(); i++)
	{
		ILayoutItem& item = items[i];
		if (item.ignored)
			continue;
		item.rect.setHeight(item.realSize.height());
	}

	while (remainHeight > 0)
	{
		int used = 0;
		for (int i = 0; i < items.size(); i++)
		{
			ILayoutItem& item = items[i];
			if (item.ignored)
				continue;
			item.rect.bottom() += 1;
			remainHeight--;
			used++;
			if (remainHeight < 1)
				break;
		}
		if (used == 0)
			break;
	}
}
void member_BVBoxLayout::applyRects()
{
	const BRect& rect = boss->rect();
	const BMargin& margin = boss->margin();
	int width = rect.width() - margin.width();

	int posX = rect.x() + margin.left();
	int posY = rect.y() + margin.top();
	for (int i = 0; i < items.size(); i++)
	{
		ILayoutItem& item = items[i];
		if (item.ignored)
			continue;
		int height = item.rect.height();
		item.adjust(posX, posY, width, height);
		posY += height + spacing;
	}
}
